/*
 * $RCSfile: DbAnyTable.java,v $
 * $Revision: 1.14 $
 *
 * Comments:
 *
 * (C) Copyright Biuro-Technika 2008.  All rights reserved.
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF Biuro-Technika
 * The copyright notice above does not evidence any
 * actual or intended publication of such source code.
 *
 * $Author: michal $          $Locker:  $
 * $Date: 2010/06/13 07:45:05 $
 * $Log: DbAnyTable.java,v $
 * Revision 1.14  2010/06/13 07:45:05  michal
 * optymalizacja petli,parametryzacja
 *
 * Revision 1.13  2010/04/13 08:02:23  michal
 * usuniecie zbednych sprawdzen w fromQuery
 *
 * Revision 1.12  2010/04/13 07:04:16  michal
 * parametryzacja kodu
 *
 * Revision 1.11  2009/11/04 13:03:23  michal
 * poprawa do czyszczenia pol przy znalezieniu klucza glownego
 *
 * Revision 1.10  2009/11/02 12:06:33  michal
 * poprawiona inicjalizacja pol przy zmianie ich wielkosci
 *
 * Revision 1.9  2009/10/23 13:38:20  michal
 * poprawki pod jave 1.4
 *
 * Revision 1.8  2009/10/23 10:28:57  michal
 * poprawki pod jave 1.4
 *
 * Revision 1.7  2009/10/23 06:50:33  michal
 * usuniecie zbednych metod
 *
 * Revision 1.6  2009/10/20 08:53:21  michal
 * poprawki pod jave 1.4
 *
 * Revision 1.5  2009/10/12 07:42:01  michal
 * przedefiniowanie metod ustawiajacych
 *
 * Revision 1.4  2009/09/22 12:22:46  michal
 * poprawa pobierania typu pol,dodanie wartosci, klucza glownego oraz numeru magnatowego tabeli
 *
 * Revision 1.3  2009/05/21 08:04:43  michal
 * drobne poprawki pod Oracle
 *
 * Revision 1.2  2009/04/20 10:44:43  michal
 * poprawa sprawdzania pustego ciagu znakowego
 *
 * Revision 1.1  2008/12/09 08:12:29  michal
 * original source code
 *
 */

package parasoft.centrap.db;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import parasoft.centrap.connection.DbConnection;

/**
 * Klasa tworz�ca obiekt tabeli wed�ug struktury tabeli w bazie danych
 *
 */
public class DbAnyTable extends DbTable {

    private final String schemaName;
//    private String  tableName;
    private boolean isLoaded;

    private static String sErrorMessage = null;

    public DbAnyTable() {
        super();
		schemaName = "";
        setSchema(schemaName);
        isLoaded = false;
    }

	private void setSchema(final String schemaName2) {
		// TODO Auto-generated method stub

	}

	public DbAnyTable(final String table) {
        super();
        setStandardTableName(table);
		schemaName = "";
        setSchema(schemaName);
        isLoaded = false;
    }

    public DbAnyTable(final String schema, final String table) {
        super();
        setStandardTableName(table);
        schemaName = schema;
        setSchema(schema);
        isLoaded = false;
    }

    public DbAnyTable(final String schema, final String table, final long nr) {
        super();
        setStandardTableName(table);
        schemaName = schema;
        setSchema(schema);
        NR = nr;
        isLoaded = false;
    }

    public void setStandardTableName(final String name){
		final String tableName = name;
    }
    public static DbAnyTable[] fromQuery(final String sql){
    	DbConnection con = null;
    	try{
			con = null;
    		return fromQuery(con,sql);
		}finally{
    		if(con !=null){
    			con.destroy();
    		}
    	}
    }
    
    public static DbAnyTable[] fromQuery(final DbConnection con, final String sql){
    	DbAnyTable[] out;
    	final Vector<DbAnyTable> vo = new Vector<DbAnyTable>();
    	if((con!=null) && (sql !=null) && (sql.trim().length()>0)){
    		try {
    			final DbAnyTable tmp = new DbAnyTable();
				final ResultSet rs = con.executeQuery(sql);
				final ResultSetMetaData rsmd = rs.getMetaData();
				final int columnsCount = rsmd.getColumnCount();
				final DbField[] fs = new DbField[columnsCount];
				final String table = "";
				final String schemaName = "";
				for(int i =1; i <= columnsCount;i++){
					final String columnName = rsmd.getColumnName(i).toUpperCase();
					final String columnAlias = rsmd.getColumnLabel(i).toUpperCase();
					int type = rsmd.getColumnType(i);
					final int columnSize = rsmd.getColumnDisplaySize(i);
					final String dataType = rsmd.getColumnTypeName(i)+((((type==Types.VARCHAR)||(type==Types.CHAR))&&(columnSize>0))?"("+columnSize+")":"");
					
                   	type = (int) DbField.sqlt2dbft(type);

                    final DbField field = new DbField(columnName, columnAlias, type, dataType);
                    fs[i-1]=field;
				}
				tmp.setFields(fs);
				
				final DbField[] flds = tmp.getFields();
				while(rs.next()){
					final DbAnyTable t = new DbAnyTable(schemaName, table){
			        	@Override
						public DbField[] getFields() {
			        		return flds;
			        	}
			        	public Object getValue(final int pos) throws DbException {
			        		return getValues()[pos];
			        	}
			        	public Object getValue(final String fieldName)
			        			throws DbException {
			                for(int i = 0; i < columnsCount; i++) {
			                    if(flds[i].getName().equalsIgnoreCase(fieldName)) {
			                        try{
			                        	return flds[i].getCorrectValue(getValues()[i]);
			                        }catch(final Exception e){e.printStackTrace();
			                        	return getValues()[i];
			                        }
			                    }
			                }
			                throw new DbException("Unknown field " + fieldName);
			        	}
			        	public int getFieldsCount() {
			        		return columnsCount;
			        	}
			        };
			        for(int i = 1; i <= columnsCount;i++){
			        	t.setValue(i-1, rs.getObject(i));
			        }
					vo.add(t);
				}
				
			} catch (final SQLException e) {
				e.printStackTrace();
			}
    	}
    	out = new DbAnyTable[vo.size()];
        vo.copyInto(out);
    	return out;
    }
    
	protected static Object[] getValues() {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * �aduje kolumny z bazy danych
	 * 
	 * @param con
	 *            obiekt po��czenia z baz�
	 * @return czy poprawnie zaladowano
	 * @throws SQLException
	 *             b��d pobrania DatabaseMetaData z po��czenia
	 */
    public boolean loadAllColumns(final DbConnection con) throws SQLException {
		return loadAllColumns(con, false);
    }
    /**
     * �aduje kolumny z bazy danych
     * @param con obiekt po��czenia z baz�
     * @param postgres czy postgres
     * @return czy poprawnie zaladowano
     * @throws SQLException
     */
    public boolean loadAllColumns(final DbConnection con, final boolean postgres) throws SQLException {
    	if(con!=null){
    		return loadAllColumns(con.getMetaData(),postgres);
    	}
    	return false;
    }

    /**
     * �aduje kolumny z bazy danych
     * @param md obiekt DatabaseMetaData
     * @return czy poprawnie zaladowano
     */
    public boolean loadAllColumns(final DatabaseMetaData md) {
		return loadAllColumns(md, false);
    }
    /**
     * �aduje kolumny z bazy danych
     * @param md obiekt DatabaseMetaData
     * @param postgres czy postgres
     * @return czy poprawnie zaladowano
     */
    public boolean loadAllColumns(final DatabaseMetaData md, final boolean postgres) {
        boolean success = false;
        ResultSet rs = null;
        try {
            if(md == null){
                sErrorMessage = "MetaData is null!";
                System.out.println(sErrorMessage);
                return false;
            }
            String column_name = null;
            int type;
            int column_size = -1;
            String column_def = null;
            boolean is_nn = false;

			final String tableName = null;
			rs = md.getColumns(null, (postgres) ? schemaName.toLowerCase()
					: schemaName, (postgres) ? tableName.toLowerCase()
					: tableName, null);
            final Vector<DbField> vFields = new Vector<DbField>();
                while(rs.next()) {
                    column_name = rs.getString("COLUMN_NAME").toUpperCase();      //String => column name
                    type = rs.getInt("DATA_TYPE");           //int => SQL type from java.sql.Types
                    column_size = rs.getInt("COLUMN_SIZE");         //int => column size.
                    column_def = rs.getString("COLUMN_DEF");       //String => default value for the column, which should be interpreted as a string when the value is enclosed in single quotes (may be null)
                    is_nn = (rs.getString("IS_NULLABLE").equalsIgnoreCase("NO"));      //String => YES|NO|empty string --- if the nullability for the parameter is unknown
                    final String dataType = dbftDataType(type, column_size, column_def, is_nn);
                   	type = (int) DbField.sqlt2dbft(type);

                    final DbField field = new DbField(column_name, type, dataType);
                    vFields.add(field);
                 success = true;
                }
                rs = md.getPrimaryKeys(null, (postgres)?schemaName.toLowerCase():schemaName, (postgres)?tableName.toLowerCase():tableName);
                String pkColumn = "";
                String pkName = null;
                while(rs.next()){
                    pkColumn = rs.getString("COLUMN_NAME");
                    pkName = rs.getString("PK_NAME");
                    if(pkName != null){
                    	fields = new DbField[vFields.size()-1];
                    	vFields.remove(new DbField(pkColumn));
                        break;
                    }
                }
                final int len = vFields.size();
               	fields = new DbField[len];
                for(int i = 0 ; i < len ; i++){
                    final DbField field = vFields.get(i);
                    fields[i] = field;
                    initDbValues();
                }
        }catch(final SQLException sqle){
            sErrorMessage = "SQL ERROR:"+sqle.getMessage();
            System.err.println(sErrorMessage);
            success = false;
        } finally {

        }
        isLoaded = true;
        return success;
    }

	private void initDbValues() {
		// TODO Auto-generated method stub

	}

	/**
	 * Konwertuje typ java.lang.Types na nazw� typu SQL z parametrami
	 * 
	 * @param sql_type
	 *            id typu
	 * @param size
	 *            wielko�� przy polach typu String
	 * @param def
	 *            do klauzuli DEFAULT
	 * @param isNotNull
	 *            czy doda� klauzul� NOT NULL
	 * @return nazw� typu SQL
	 */
    public static String dbftDataType(final int sql_type, final int size,final String def, final boolean isNotNull){
        String dataType ="";
        final Integer key = new Integer(sql_type);
        final int iKey = key.intValue();
		dataType = "" + (iKey);
        if(dataType==null){
	        switch(iKey){
	            case Types.VARCHAR:
	            case Types.CHAR:
	            if(size > 0){
	                dataType = DbField.dbftString(size);
	            }else{
	                dataType = DbField.dbftString();
	            }
	            break;
	            case Types.BIGINT:
	            	dataType = DbField.dbftBigint();
	            break;
	            default:
				dataType = "" + (iKey);
	            break;
	        }
        }
        if((def != null) && (def.trim().length()>0)){
            dataType += " DEFAULT " + def.toUpperCase();
        }

        if(isNotNull){
            dataType += " NOT NULL";
        }
        return dataType;
    }

    public String getStandardTableName()
    {
		final String tableName = null;
		return tableName;
    }
    public DbField getKeyField()
    {
        return key;
    }

    public DbField[] getFields()
    {
        if(fields == null){
            return new DbField[0];
        }
        return fields;
    }

    /**
     * Ustawia pola dla obiektu tabeli
     * @param fs tablica p�l
     */
    public void setFields(final DbField[] fs){
    	fields = fs;
    	initDbValues();
    }

    /**
     * Ustawia pole klucza g��wnego.<br>
     * Usuwa klucz g��wny je�eli znajdzie go w standardowych polach obietku
     * @param f pole
     */
    public void setKeyField(final DbField f){
    	if(fields!=null){
    		final int len = fields.length;
	    	final Vector<DbField> vf = new Vector<DbField>(len);
	    	for (final DbField fld : fields) {
				if (!fld.equals(f)) {
					vf.add(fld);
				}
			}
	    	fields = new DbField[vf.size()];
	    	vf.copyInto(fields);
	    	initDbValues();
    	}
    	key = f;
    }

    /**
     * Dodaje pole do puli p�l
     * @param f dodaje pole
     */
    public void addField(final DbField f){
    	if((fields!=null) && (f!=null)){
	    	final List<DbField> l = new ArrayList<DbField>();
	    	for (final DbField fld : fields) {
	    		l.add(fld);
	    	}
	    	if(!l.contains(f)){
	    		l.add(f);
	    	}
	    	l.toArray(fields);
	    	initDbValues();
    	}
    }

    /**
     * Usuwa pole z puli p�l
     * @param f pole do usuni�cia
     * @return instancja pola, kt�re jest usuwane lub null w przypadku prawid�owego usuni�cia
     */
    public DbField removeField(final DbField f){
    	if((fields!=null) && (f!=null)){
	    	final List<DbField> l = new ArrayList<DbField>();
	    	for (final DbField fld : fields) {
	    		l.add(fld);
	    	}
	    	if(!l.contains(f)){
	    		return f;
	    	}else {
	    		l.remove(f);
	    		l.toArray(fields);
	    		initDbValues();
	    		return null;
	    	}
    	}
    	return f;
    }

    /**
     * Wzraca wiadomo�� b��du, kt�ra powsta�a podczas �adowania kolumn obiektu
     * @return b��d
     */
    public String getErrorMessage()
    {
        return sErrorMessage != null ? sErrorMessage : "";
    }

    /**
     * Czy obiekt jest za�adowany
     */
    public boolean isLoaded(){
        return isLoaded;
    }

    /*** @see parasoft.centrap.db.DbAnyTable*/
    @Override
	public void setValue(final int pos, final char value) {
    	super.setValue(pos, value);
    }
    /*** @see parasoft.centrap.db.DbAnyTable*/
    @Override
	public void setValue(final int pos, final double v) {
    	super.setValue(pos, v);
    }
    /*** @see parasoft.centrap.db.DbAnyTable*/
    @Override
	public void setValue(final int pos, final Object value) {
    	super.setValue(pos, value);
    }
    /*** @see parasoft.centrap.db.DbAnyTable*/
    @Override
	public void setValue(final String fieldName, final Object value) throws DbException {
    	super.setValue(fieldName, value);
    }
    /**
     * Ustawia numer magnatowy tabeli
     * @param nr numer tabeli
     */
    public void setTableNr(final long nr){
    	NR = nr;
    }

   	/* (non-Javadoc)
   	 * @see parasoft.centrap.db.DbTable#getTableNr()
   	 */
   	public long getTableNr() {
		return NR;
	}

    private static DbField key = new DbField("");
    private static DbField fields[] = null;
    private long NR = -2;
}
